package handlers

import (
	"log"
	"npxy-go/pkg/tcp"
	"npxy-go/pkg/util/converter"
)

const magicNumAuthorize uint16 = 9527

type authorizeMessage struct {
	state  int8
	secret string
}

func (a *authorizeMessage) decode(buf *tcp.Buffer) {
	a.state = buf.ReadInt8()
	a.secret = converter.BytesToStr(buf.ReadAll())
}

func (a *authorizeMessage) encode(buf *tcp.Buffer) {
	buf.AppendBytes(converter.StrToBytes(a.secret))
	buf.AddInt8(a.state)
	buf.AddUInt16(magicNumAuthorize)
}

func (a *authorizeMessage) Ok() bool {
	return a.state == 1
}

type AuthorizeHandler struct {
	tcp.BaseHandler
	authorized bool
	isServer   bool
	secret     string
}

func NewServerAuthorizeHandler(secret string)*AuthorizeHandler  {
	return newAuthorizeHandler(true, secret)
}

func NewClientAuthorizeHandler(secret string)*AuthorizeHandler  {
	return newAuthorizeHandler(false, secret)
}

func newAuthorizeHandler(isServer bool, secret string) *AuthorizeHandler {
	return &AuthorizeHandler{isServer: isServer, secret: secret}
}

func (a *AuthorizeHandler) OnMessage(ctx *tcp.SessionContext, buf *tcp.Buffer) {
	if a.authorized {
		ctx.NextOnMessage(buf)
		return
	}

	if buf.ReadUInt16() != magicNumAuthorize {
		log.Println("Unauthorized:", ctx.String())
		ctx.Close()
		return
	}

	var m authorizeMessage
	m.decode(buf)

	if a.isServer == true {
		a.handleServer(ctx, buf, m)
	} else {
		a.handleClient(ctx, m)
	}
}

func (a *AuthorizeHandler) OnConnected(ctx *tcp.SessionContext) {
	if a.isServer == false {
		buf := tcp.NewBuffer()
		msg := authorizeMessage{secret: a.secret}
		msg.encode(buf)
		ctx.PreWrite(buf)
	}
}

func (a *AuthorizeHandler) OnDisconnected(ctx *tcp.SessionContext) {
	if a.authorized {
		if ctx.Next != nil {
			ctx.SetCurrentNode(ctx.Next)
			ctx.MessageHandler.OnDisconnected(ctx)
		}
	}
}

func (a *AuthorizeHandler) handleClient(ctx *tcp.SessionContext, m authorizeMessage) {
	if m.Ok() {
		a.authorized = true
		log.Println("Approved", ctx.String())
		if handler := ctx.NextHandler(); handler != nil {
			node := ctx.CurrentNode()
			ctx.SetCurrentNode(node.Next)
			handler.OnConnected(ctx)
		}
	} else {
		log.Println("Rejected", ctx.String())
		ctx.Close()
	}
}

func (a *AuthorizeHandler) handleServer(ctx *tcp.SessionContext, buf *tcp.Buffer, m authorizeMessage) {
	if m.secret == a.secret {
		a.authorized = true
		log.Println("Approved", ctx.String())
		a.approve(ctx, buf, &m)
	} else {
		a.reject(ctx, buf, &m)
		log.Println("Rejected", ctx.String())
		ctx.Close()
	}
}

func (a *AuthorizeHandler) reject(ctx *tcp.SessionContext, buf *tcp.Buffer, m *authorizeMessage) {
	m.state = 0
	m.encode(buf)
	ctx.PreWrite(buf)
}

func (a *AuthorizeHandler) approve(ctx *tcp.SessionContext, buf *tcp.Buffer, m *authorizeMessage) {
	if handler := ctx.NextHandler(); handler != nil {
		//reply
		m.state = 1
		m.encode(buf)
		node := ctx.CurrentNode()
		ctx.PreWrite(buf)

		//on connected
		ctx.SetCurrentNode(node.Next)
		handler.OnConnected(ctx)
	}
}
